package com.huanxing.cloud.pay.api.service.impl;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyV3Response;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result;
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyV3Result;
import com.github.binarywang.wxpay.config.WxPayConfig;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.huanxing.cloud.common.core.constant.CommonConstants;
import com.huanxing.cloud.common.core.constant.RocketMqConstants;
import com.huanxing.cloud.pay.api.config.WxPayConfiguration;
import com.huanxing.cloud.pay.api.mapper.PayNotifyRecordMapper;
import com.huanxing.cloud.pay.api.mapper.PayRefundOrderMapper;
import com.huanxing.cloud.pay.api.mapper.PayTradeOrderMapper;
import com.huanxing.cloud.pay.api.service.IPayNotifyRecordService;
import com.huanxing.cloud.pay.common.constants.PayConstants;
import com.huanxing.cloud.pay.common.entity.PayNotifyRecord;
import com.huanxing.cloud.pay.common.entity.PayRefundOrder;
import com.huanxing.cloud.pay.common.entity.PayTradeOrder;
import com.huanxing.cloud.pay.common.enums.PayRefundOrderStatusEnum;
import lombok.RequiredArgsConstructor;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.time.Duration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * 支付订单
 *
 * @author lijx
 * @date 2022/6/16
 */
@Service
@RequiredArgsConstructor
public class PayNotifyRecordServiceImpl extends ServiceImpl<PayNotifyRecordMapper, PayNotifyRecord>
		implements IPayNotifyRecordService {

	private final RocketMQTemplate rocketMQTemplate;

	private final RedisTemplate redisTemplate;

	private final PayTradeOrderMapper payTradeOrderMapper;

	private final PayRefundOrderMapper payRefundOrderMapper;

	@Override
	public String wxPayNotify(String tenantId, String notifyData) {
		// 解密微信支付回调
		WxPayConfig wxPayConfig = new WxPayConfig();
		WxPayOrderNotifyV3Result wxPayOrderNotifyV3Result = null;
		try {
			wxPayOrderNotifyV3Result = WxPayConfiguration.wxPayService(wxPayConfig)
				.parseOrderNotifyV3Result(notifyData, null);
		}
		catch (WxPayException e) {
			return WxPayNotifyV3Response.success("解密失败");
		}
		WxPayOrderNotifyV3Result.DecryptNotifyResult decryptNotifyResult = wxPayOrderNotifyV3Result.getResult();

		final String outTradeNo = decryptNotifyResult.getOutTradeNo();

		boolean bIfAbsent = redisTemplate.opsForValue().setIfAbsent(outTradeNo, outTradeNo, Duration.ofSeconds(10L));
		if (bIfAbsent) {
			PayNotifyRecord payNotifyRecord = new PayNotifyRecord();
			payNotifyRecord.setRequest(JSON.toJSONString(decryptNotifyResult));
			payNotifyRecord.setResponse("重复回调");
			payNotifyRecord.setOutTradeNo(outTradeNo);
			payNotifyRecord.setChannelOrderNo(decryptNotifyResult.getTransactionId());
			payNotifyRecord.setType(PayConstants.PAY_NOTIFY_TYPE);
			this.save(payNotifyRecord);
			return WxPayNotifyV3Response.fail("重复回调");
		}
		// 查询订单
		PayTradeOrder orderInfo = payTradeOrderMapper
			.selectOne(Wrappers.<PayTradeOrder>lambdaQuery().eq(PayTradeOrder::getOutTradeNo, outTradeNo));
		if (ObjectUtil.isNull(orderInfo)) {
			return WxPayNotifyV3Response.fail("order not found! orderNo: " + outTradeNo);
		}
		orderInfo.setChannelOrderNo(decryptNotifyResult.getTransactionId());
		orderInfo.setPayStatus(CommonConstants.YES);
		orderInfo.setPaySuccessTime(
				LocalDateTimeUtil.parse(decryptNotifyResult.getSuccessTime(), "yyyy-MM-dd'T'HH:mm:ssXXX").toString());
		payTradeOrderMapper.updateById(orderInfo);
		// 保存回调记录
		PayNotifyRecord payNotifyRecord = new PayNotifyRecord();
		payNotifyRecord.setRequest(JSON.toJSONString(decryptNotifyResult));
		payNotifyRecord.setResponse(WxPayNotifyV3Response.success("成功"));
		payNotifyRecord.setOutTradeNo(decryptNotifyResult.getOutTradeNo());
		payNotifyRecord.setChannelOrderNo(decryptNotifyResult.getTransactionId());
		payNotifyRecord.setType(PayConstants.PAY_NOTIFY_TYPE);
		this.save(payNotifyRecord);
		// rocketmq 通知
		JSONObject jsonObject = new JSONObject();
		jsonObject.put(PayConstants.OUT_TRADE_NO, payNotifyRecord.getOutTradeNo());
		jsonObject.put(PayConstants.PAY_SUCCESS_TIME, orderInfo.getPaySuccessTime());
		jsonObject.put(PayConstants.EXTRA_PARAMS, orderInfo.getExtra());
		jsonObject.put(PayConstants.TENANT_ID, orderInfo.getTenantId());
		rocketMQTemplate.syncSend(RocketMqConstants.PAY_NOTIFY_TOPIC, new GenericMessage<>(jsonObject),
				RocketMqConstants.TIME_OUT);
		return WxPayNotifyV3Response.success("成功");
	}

	@Override
	public String aliPayNotify(String tenantId, HttpServletRequest request) {
		Map<String, String> params = new HashMap<>();
		Map requestParams = request.getParameterMap();
		for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext();) {
			String name = (String) iter.next();
			String[] values = (String[]) requestParams.get(name);
			String valueStr = "";
			for (int i = 0; i < values.length; i++) {
				valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ",";
			}
			// 乱码解决，这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化
			// valueStr = new String(valueStr.getBytes("ISO-8859-1"), "gbk");
			params.put(name, valueStr);
		}
		// 获取支付宝的通知返回参数，可参考技术文档中页面跳转同步通知参数列表(以下仅供参考)//
		// 商户订单号
		String outTradeNo = request.getParameter("out_trade_no");
		// 交易状态
		String tradeStatus = request.getParameter("trade_status");
		// 总退款金额
		String refundFee = request.getParameter("refund_fee");
		// 退款单号
		String outBizNo = request.getParameter("out_biz_no");

		boolean bIfAbsent = redisTemplate.opsForValue().setIfAbsent(outTradeNo, outTradeNo, Duration.ofSeconds(10L));
		if (bIfAbsent) {
			PayNotifyRecord payNotifyRecord = new PayNotifyRecord();
			payNotifyRecord.setRequest(JSON.toJSONString(params));
			payNotifyRecord.setResponse("重复回调");
			payNotifyRecord.setOutTradeNo(outTradeNo);
			payNotifyRecord.setChannelOrderNo(outTradeNo);
			payNotifyRecord.setType(PayConstants.PAY_NOTIFY_TYPE);
			this.save(payNotifyRecord);
			return PayConstants.ALIPAY_FAIL;
		}

		if (StrUtil.isEmpty(outBizNo) && StrUtil.isEmpty(refundFee)) {
			// 支付回调

			// 获取支付宝的通知返回参数，可参考技术文档中页面跳转同步通知参数列表(以上仅供参考)//
			// 计算得出通知验证结果
			// boolean verify_result = AlipaySignature.rsaCheckV1(params,
			// AlipayConfig.ALIPAY_PUBLIC_KEY, AlipayConfig.CHARSET, "RSA2");
			if ("TRADE_SUCCESS".equals(tradeStatus) || "TRADE_FINISHED".equals(tradeStatus)) {
				// 查询订单
				PayTradeOrder orderInfo = payTradeOrderMapper
					.selectOne(Wrappers.<PayTradeOrder>lambdaQuery().eq(PayTradeOrder::getOutTradeNo, outTradeNo));
				if (ObjectUtil.isNull(orderInfo)) {
					log.error("order not found! orderNo:" + outTradeNo);
					return PayConstants.ALIPAY_FAIL;
				}
				orderInfo.setChannelOrderNo(outTradeNo);
				orderInfo.setPayStatus(CommonConstants.YES);
				orderInfo.setPaySuccessTime(
						LocalDateTimeUtil.parse(request.getParameter("gmt_payment"), DatePattern.NORM_DATETIME_PATTERN)
							.toString());
				payTradeOrderMapper.updateById(orderInfo);
				// 保存回调记录
				PayNotifyRecord payNotifyRecord = new PayNotifyRecord();
				payNotifyRecord.setRequest(JSON.toJSONString(params));
				payNotifyRecord.setResponse(PayConstants.ALIPAY_SUCCESS);
				payNotifyRecord.setOutTradeNo(outTradeNo);
				payNotifyRecord.setChannelOrderNo(outTradeNo);
				payNotifyRecord.setType(PayConstants.PAY_NOTIFY_TYPE);
				this.save(payNotifyRecord);
				// rocketmq 通知
				JSONObject jsonObject = new JSONObject();
				jsonObject.put(PayConstants.OUT_TRADE_NO, payNotifyRecord.getOutTradeNo());
				jsonObject.put(PayConstants.PAY_SUCCESS_TIME, orderInfo.getPaySuccessTime());
				jsonObject.put(PayConstants.EXTRA_PARAMS, orderInfo.getExtra());
				jsonObject.put(PayConstants.TENANT_ID, orderInfo.getTenantId());
				rocketMQTemplate.syncSend(RocketMqConstants.PAY_NOTIFY_TOPIC, new GenericMessage<>(jsonObject),
						RocketMqConstants.TIME_OUT);
			}
		}

		return PayConstants.ALIPAY_SUCCESS;
	}

	@Override
	public String wxPayRefundNotify(String tenantId, String params) {
		WxPayConfig wxPayConfig = new WxPayConfig();
		WxPayRefundNotifyV3Result wxPayRefundNotifyV3Result = null;
		try {
			wxPayRefundNotifyV3Result = WxPayConfiguration.wxPayService(wxPayConfig)
				.parseRefundNotifyV3Result(params, null);

		}
		catch (WxPayException e) {
			return WxPayNotifyV3Response.fail("解密失败");
		}
		WxPayRefundNotifyV3Result.DecryptNotifyResult result = wxPayRefundNotifyV3Result.getResult();
		final String outRefundNo = result.getOutRefundNo();
		boolean bIfAbsent = redisTemplate.opsForValue().setIfAbsent(outRefundNo, outRefundNo, Duration.ofSeconds(10L));
		if (bIfAbsent) {
			PayNotifyRecord payNotifyRecord = new PayNotifyRecord();
			payNotifyRecord.setRequest(JSON.toJSONString(params));
			payNotifyRecord.setResponse("重复回调");
			payNotifyRecord.setOutTradeNo(outRefundNo);
			payNotifyRecord.setChannelOrderNo(result.getRefundId());
			payNotifyRecord.setType(PayConstants.REFUND_NOTIFY_TYPE);
			this.save(payNotifyRecord);
			return WxPayNotifyV3Response.fail("重复回调");
		}
		PayRefundOrder payRefundOrder = payRefundOrderMapper.selectOne(
				Wrappers.<PayRefundOrder>lambdaQuery().eq(PayRefundOrder::getRefundTradeMo, result.getOutRefundNo()));
		if (ObjectUtil.isNull(payRefundOrder)) {
			return WxPayNotifyV3Response.fail("order not found! orderNo: " + result.getOutRefundNo());
		}
		payRefundOrder.setChannelRefundNo(result.getRefundId());
		payRefundOrder.setRefundStatus(PayRefundOrderStatusEnum.STATUS_2.getCode());
		payRefundOrderMapper.updateById(payRefundOrder);
		// 保存回调记录
		PayNotifyRecord payNotifyRecord = new PayNotifyRecord();
		payNotifyRecord.setRequest(JSON.toJSONString(result));
		payNotifyRecord.setResponse(WxPayNotifyV3Response.success("成功"));
		payNotifyRecord.setOutTradeNo(result.getOutRefundNo());
		payNotifyRecord.setChannelOrderNo(result.getRefundId());
		payNotifyRecord.setType(PayConstants.REFUND_NOTIFY_TYPE);
		this.save(payNotifyRecord);
		// rocketmq 通知
		JSONObject jsonObject = new JSONObject();
		jsonObject.put(PayConstants.EXTRA_PARAMS, payRefundOrder.getExtra());
		jsonObject.put(PayConstants.REFUND_TRADE_NO, payRefundOrder.getRefundTradeMo());
		jsonObject.put(PayConstants.TENANT_ID, payRefundOrder.getTenantId());
		rocketMQTemplate.syncSend(RocketMqConstants.PAY_REFUND_NOTIFY_TOPIC, new GenericMessage<>(jsonObject),
				RocketMqConstants.TIME_OUT);
		return WxPayNotifyV3Response.success("成功");
	}

}
